#pragma once

/*
 * pipe config.
 */

#include <string>
#include <vector>
#include <map>
#include "string.h"
#include "pipe_define.h"

namespace anet {
	namespace pipe {
		// utility functions.
		inline std::string trimRight(const std::string& strSrc, char c) {
			const char* pEnd = strSrc.c_str() + strSrc.length();
			pEnd--;
			while (*pEnd == c && pEnd >= strSrc.c_str()) {
				pEnd--;
			}
			return std::string(strSrc.c_str(), pEnd + 1);
		}
		inline std::string trimLeft(const std::string& strSrc, char c) {
			const char* pStart = strSrc.c_str();
			while (*pStart == c && (pStart < strSrc.c_str() + strSrc.length())) {
				pStart++;
			}
			return std::string(pStart, strSrc.c_str() + strSrc.length());
		}
		inline void strSplits(const std::string& strData,
			const std::string& strDelim,
			std::vector<std::string>& vecData
		) {
			vecData.reserve(5);
			const char* pszNextData = strData.c_str();
			const char* pszPos = nullptr;
			while ((pszPos = strstr(pszNextData, strDelim.c_str()))) {
				auto dst = trimLeft(std::string(pszNextData, pszPos - pszNextData), '\n');
				dst = trimLeft(dst,' ');
				dst = trimRight(dst, ' ');
				vecData.push_back(dst);
				pszNextData = pszPos + 1;
			}

			/* push last substring only if it is not empty*/
			if (pszNextData) {
				std::string strData = trimLeft(trimRight(pszNextData, ' '), '\n');
				strData = trimLeft(strData, ' ');
				strData = trimRight(strData, ' ');
				if (!strData.empty()) {
					vecData.push_back(strData);
				}
			}
		}

		// server node information.
		struct SNodeInfo {
			std::string name;
			uint32      id;
			std::string listenIP;
			uint16      listenPort;
		};

		// service discovery's redis information.
		struct SServiceDiscoveryInfo {
			std::string ip;
			uint16      port;
			int32       index;
			std::string password;
			std::string key;
		};

		// node connection relation information.
		struct SRelationInfo {
			std::string key;
			std::vector<std::string> connections;
		};

		using serviceDiscoveryDataType = anet::pipe::SServiceDiscoveryInfo;
		using nodeRelationInfo = anet::pipe::SRelationInfo;

		// pipe config from xml.
		class CPipeConfig final {
		public:
			CPipeConfig();
			virtual ~CPipeConfig();
			CPipeConfig(const CPipeConfig& rhs) = delete;
			CPipeConfig& operator=(const CPipeConfig& rhs) = delete;

		public:
			bool Load(const char* filePath);
			uint32 GetServerId() const;
			const std::string& GetServerName() const;
			std::string GetListenAddr() const;
			const std::string& GetListenIP() const;
			uint16 GetListenPort() const;
			const serviceDiscoveryDataType& GetServiceDiscoveryInfo() const;
			bool IsConnectTo(const std::string &nodeName, uint32 nodeId) const;
			const std::string& GetNodeToken(const std::string& nodeName) const;

		protected:
			bool tryToFind(const std::string& nodeName) const;
			bool checkConfig() const;
			const nodeRelationInfo* findWith(const std::string& nodeName) const;

		private:
			SNodeInfo m_nodeInfo;
			SServiceDiscoveryInfo m_serviceDiscoveryInfo;
			std::map<std::string, SRelationInfo> m_relations;
		};
	}
}